package fr.uvsq.isty.bdh.hchess.uci;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;


/**
 * A command sent by the chess engine. The first "word" is the command type,
 * for example "bestmove", and then the rest are the parameters, for example
 * ["e3e4","ponder","c7c6"].
 * @author Clement Bellot <clementbellot@gmail.com>
 */
public final class UciCommand {
    private ArrayList<String> tokens;
    public UciCommand(String line) throws IOException {
        this(new StringReader(line));
    }
    /**
     * Construct a command from a reader. Is blocking if there is no data
     * sent by the reader.
     * @param r The reader from which to construct the command.
     * @throws IOException 
     */
    public UciCommand(Reader r) throws IOException{
        tokens = new ArrayList<String>();
        String currentToken = "";
        int currentChar;
        System.out.print("Command recived : ");
        while(true){
            currentChar = r.read();
            System.out.print((char)currentChar);
            if(currentChar==-1 || currentChar=='\n'){// EOL or EOF.
                if(!currentToken.isEmpty()){
                    tokens.add(currentToken);
                }
                break;
            }else if(currentChar==' '){
                if(currentToken.isEmpty()){
                    throw new IOException("Bad command.");
                }
                tokens.add(currentToken);
                currentToken = "";
            }else{
                if(currentChar != '\r'){// Windows hack.
                    currentToken += (char)currentChar;
                }
            }
        }
        if(tokens.isEmpty()){
            System.out.println("Bad command.");
            throw new IOException("Bad input : Empty line.");
        }
    }
    /**
     * 
     * @return The command, "bestmove" fro example.
     */
    public String getCommand(){
        return tokens.get(0);
    }
    /**
     * The command's paramaters. There are <code>getParametersCount()</code>
     * parameters.
     * Example: ["e3e4","ponder","c7c6"].
     * @param index The parameters index, [0,n[.
     * @return The parameter.
     */
    public String getParameter(int index) throws IndexOutOfBoundsException{
        return tokens.get(index+1);
    }
    /**
     * @return The command parameter count. Without the command.
     */
    public int getParametersCount(){
        return tokens.size()-1;
    }
    /**
     * This non blocking method tries to get the last command sent by the
     * engine. For this, it takes a timeout. It shall be a time after which, if
     * the command hasn't been sent, it can be reasonably considered it won't
     * be sent. It is not exact, as it does not check that the time actually
     * passed. Please also make sure that the thread in which this method is
     * called is never interupted.
     * 
     * If the command was found as the last command sent, it is returned, in
     * case it(s) parameter(s) are needed. Else, an exception is thrown.
     * @param reader The stream from which to read the commands.
     * @param wishedCommand The wished command.
     * @param millisecondsTimeout The time before which the command start to be
     * read.
     * @return The command of the right type, or null if there was no such
     * command or if is wasn't the last one.
     * @throws IOException If the reader fails, or if the expected input is not
     * found.
     */
    static UciCommand expectLastWithTimeout(
            InputStreamReader reader,
            String wishedCommand,
            int millisecondsTimeout) throws IOException{
        UciCommand ret = null;
        try {
            Thread.sleep(millisecondsTimeout);
        } catch (InterruptedException ex) {
            assert false : "Nobody should interupt me.";
        }
        while(reader.ready()){
            ret = new UciCommand(reader);
        }
        if(ret==null || !ret.getCommand().equals(wishedCommand)){
            throw new IOException("The expected command was not found.");
        }else{
            return ret;
        }
    }
}
